My basic code implementing the Fidenza algorithm is in the README.Rmd file in 04-fidenza.

Here I want to play with it a bit more.

For example, I used the length of the path but could use the sequence of steps to modify some aesthetic.

I will use the following packages:

library(ggforce)
Loading required package: ggplot2
library(MexBrewer)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages -------------------------------------------------------------------------------------------------------- tidyverse 1.3.1 --
v tibble  3.1.6     v dplyr   1.0.7
v tidyr   1.1.4     v stringr 1.4.0
v readr   2.1.1     v forcats 0.5.1
v purrr   0.3.4     
-- Conflicts ----------------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

This are the basic steps:

  1. Generate a grid
  1. Generate a flow field on the grid
  1. Generate paths

Generate Grid

# Size of grid
n_min <- -17 
n_max <- 18
# Length of segment
l <- sqrt(2) + 0.1

df <- data.frame(expand.grid(x = seq(n_min, n_max, 1), y = seq(n_min, n_max, 1)))

Create and plot the flow field:

Another possible parameter is using the valence of the coordinates to alter the angle:

# Offsets
x_o <- -0
y_o <- -0

df <- df %>%
  mutate(angle = ((x - x_o) * (y - y_o)) /(n_max - n_min)^2 * 2 * pi * sign(x * y), # add the signs
         xend = (x + l * cos(angle)),
         yend = (y + l * sin(angle)))

df %>%
  ggplot() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend)) +
  geom_point(aes(x = xend, 
                 y =yend)) +
  coord_equal() +
  theme_void()

The valence could be used to change the direction of the displacement of the coordinate, instead of the angle:

# Offsets
x_o <- -0
y_o <- -0

df <- df %>%
  mutate(angle = ((x - x_o) * (y - y_o)) /(n_max - n_min)^2 * 2 * pi, # add the signs
         xend = (x + sign(x) * l * cos(angle)),
         yend = (y + sign(y) * l * sin(angle)))

df %>%
  ggplot() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend)) +
  geom_point(aes(x = xend, 
                 y =yend)) +
  coord_equal() +
  theme_void()

Another possible parameter is an initial rotation:

# Offsets
x_o <- -0
y_o <- -0
init_r <- 1/8

df <- df %>%
  mutate(angle = ((x - x_o) * (y - y_o)) /(n_max - n_min)^2 * 2 * pi + init_r * pi, # add the signs
         xend = (x + l * cos(angle)),
         yend = (y + l * sin(angle)))

df %>%
  ggplot() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend)) +
  geom_point(aes(x = xend, 
                 y =yend)) +
  coord_equal() +
  theme_void()

This one has an initial rotation and valence for the angle:

# Offsets
x_o <- -0
y_o <- -0
init_r <- 1/8

df <- df %>%
  mutate(angle = (((x - x_o) * (y - y_o)) /(n_max - n_min)^2 * 2 * pi + init_r * pi) * sign(x * y), # add the signs
         xend = (x + l * cos(angle)),
         yend = (y + l * sin(angle)))

df %>%
  ggplot() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend)) +
  geom_point(aes(x = xend, 
                 y =yend)) +
  coord_equal() +
  theme_void()

This one has an initial rotation and valence for the displacement:

# Offsets
x_o <- -0
y_o <- -0
init_r <- 1/8

df <- df %>%
  mutate(angle = ((x - x_o) * (y - y_o)) /(n_max - n_min)^2 * 2 * pi + init_r * pi, # add the signs
         xend = (x + sign(x) * l * cos(angle)),
         yend = (y + sign(y) * l * sin(angle)))

df %>%
  ggplot() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend)) +
  geom_point(aes(x = xend, 
                 y =yend)) +
  coord_equal() +
  theme_void()

This one has an initial rotation, valence for the displacement and valence for the angle:

# Offsets
x_o <- -0
y_o <- -0
init_r <- 1/8

df <- df %>%
  mutate(angle = (((x - x_o) * (y - y_o)) /(n_max - n_min)^2 * 2 * pi + init_r * pi) * sign(x * y), # add the signs
         xend = (x + sign(x) * l * cos(angle)),
         yend = (y + sign(y) * l * sin(angle)))

df %>%
  ggplot() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend)) +
  geom_point(aes(x = xend, 
                 y =yend)) +
  coord_equal() +
  theme_void()

Make paths using the last flow field:

# Set seed for reproducibility
#set.seed(346363)

# Parameters for creating the paths
res <- 0.2 # Resolution for the path, smaller numbers create more points per path/smoother paths
n_iter <- 20 # Number of iterations per path
n_path <- 500 # Number of paths

# Sample starting points
st_point <- data.frame(x = runif(n_path, n_min, n_max),
                       #x = 2,
                       y = runif(n_path, n_min, n_max))

# Initialize data frame with paths
paths <- data.frame(x = numeric(), y = numeric(), step = numeric(), path = numeric())

for(p in 1:n_path){
  paths <- rbind(paths,
                 data.frame(st_point[p,], step = 1, path = p))
  next_x <- st_point[p, "x"]
  next_y <- st_point[p, "y"]
  for(i in 1:n_iter){
    # With the next point, retrieve the angle of the closest point in the direction of the flow
    angle <- df %>% 
      mutate(d = sqrt((x - next_x)^2 + (y - next_y)^2)) %>%
      filter(d == min(d)) %>%
      pull(angle)
    # Calculate the coordinates of the new next point and bind to paths
    paths <- rbind(paths,
                   data.frame(x = next_x + sign(next_x) * res * cos(angle), # Important to multiply by the sign of the next point
                              y = next_y + sign(next_y) * res * sin(angle), # Important to multiply by the sign of the next point
                              step = i + 1,
                              path = p))
    # Find the next starting point for the search
    next_x <- slice_tail(paths) %>%
      pull(x)
    next_y <- slice_tail(paths) %>%
      pull(y)
    
    # Check the boundary condition
    boundary_condition <- next_x < n_min | next_x >= n_max |
      next_y < n_min | next_y >= n_max
    # If at the boundary set i to n_iter to complete the loop
    if (boundary_condition) break
  }
}

# Rename paths

paths_ex3 <- paths

Plot paths:

ggplot() + 
  geom_line(data = paths_ex3,
            aes(x = x,
                y = y,
                group = path),
            color = "blue",
            size = 1) +
  geom_segment(data = df,
               aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend)) +
  geom_point(data = df,
             aes(x = xend, 
                 y =yend)) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

Instead of lines use symbols:

ggplot() + 
  geom_point(data = paths_ex3 %>%
               mutate(p_grid = (x * y)^2),
             aes(x = x,
                 y = y,
                 group = path, 
                 color =p_grid),
             size = 1) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

Add length of path to table:

paths_ex3 <- paths_ex3 %>% 
  left_join(paths_ex3 %>%
              group_by(path) %>%
              summarize(l_path = n(), # length of path
                        .groups = "drop"),
            by = "path") 

Change the size of the symbols based on their position in the path:

paths_ex3 <- paths_ex3 %>%
  mutate(p_grid = (x * y)^2,
         size = 1 / (abs(step - l_path/2) + 1))

ggplot(data = paths_ex3) + 
  geom_circle(aes(x0 = x,
                  y0 = y, 
                  fill =p_grid,
                  color = p_grid,
                  r = size * res),
              size = 1) +
  geom_point(aes(x = x - 0.7 * size * res * pi/4,
                 y = y + 0.7 * size * res * pi/4),
             color = "white", 
             size = 0.1) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

Make pretty with palette Ronda:

col_palette <- mex.brewer("Ronda")

paths_ex3 <- paths_ex3 %>%
  mutate(p_grid = (x * y)^2,
         size = 1 / (abs(step - l_path/2) + 1))

ggplot() + 
  geom_rect(aes(xmin = n_min, 
                xmax = n_max, 
                ymin = n_min, 
                ymax = n_max),
            fill = "black") +
  geom_circle(data = paths_ex3,
              aes(x0 = x,
                  y0 = y,
                  fill =p_grid,
                  color = p_grid,
                  r = 1.2 * size * res)) +
  geom_point(data = paths_ex3,
             aes(x = x - 1.2 * size * res * pi/4,
                 y = y + 1.2 * size * res * pi/4),
             color = col_palette[4],
             size = 0.1) +
  geom_circle(aes(x0 = 0,
                  y0 = 0,
                  r = 4),
              color = col_palette[4],
              fill = "black",
              size = 2) +
  scale_color_gradientn(colors = col_palette) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

Make pretty with palette Revolucion. The color depends on the position in the grid:

col_palette <- mex.brewer("Revolucion")

paths_ex3 <- paths_ex3 %>%
  mutate(p_grid = (x * y)^2,
         size = 1 / (abs(step - l_path/2) + 1))

ggplot() + 
  geom_rect(aes(xmin = n_min, 
                xmax = n_max, 
                ymin = n_min, 
                ymax = n_max),
            fill = "black") +
  geom_circle(data = paths_ex3,
              aes(x0 = x,
                  y0 = y,
                  fill =p_grid,
                  color = p_grid,
                  r = 1.2 * size * res)) +
  geom_point(data = paths_ex3,
             aes(x = x - 1.2 * size * res * pi/4,
                 y = y + 1.2 * size * res * pi/4),
             color = col_palette[4],
             size = 0.1) +
  geom_circle(aes(x0 = 0,
                  y0 = 0,
                  r = 4),
              color = col_palette[4],
              fill = "black",
              size = 2) +
  scale_color_gradientn(colors = col_palette) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

Make pretty with palette Revolucion but now change the rate of change of the colors:

col_palette <- mex.brewer("Revolucion")

paths_ex3 <- paths_ex3 %>%
  mutate(p_grid = (x * y)^2,
         size = 1 / (abs(step - l_path/2) + 1))

ggplot() + 
  geom_rect(aes(xmin = n_min, 
                xmax = n_max, 
                ymin = n_min, 
                ymax = n_max),
            fill = "black") +
  geom_circle(data = paths_ex3,
              aes(x0 = x,
                  y0 = y,
                  fill = p_grid^(1/3),
                  color = p_grid^(1/3),
                  r = 1.2 * size * res)) +
  geom_point(data = paths_ex3,
             aes(x = x - 1.2 * size * res * pi/4,
                 y = y + 1.2 * size * res * pi/4),
             color = col_palette[4],
             size = 0.1) +
  geom_segment(aes(x = n_min, xend = n_max, y = 0, yend = 0),
             color = col_palette[4],
             size = 3) +
  geom_segment(aes(x = 0, xend = 0, y = n_min, yend = n_max),
             color = col_palette[4],
             size = 3) +
  geom_circle(aes(x0 = 0,
                  y0 = 0,
                  r = 4),
              color = col_palette[4],
              fill = "black",
              size = 3) +
  geom_rect(aes(xmin = n_min, 
                xmax = n_max, 
                ymin = n_min, 
                ymax = n_max),
            fill = NA,
            color = col_palette[4], 
            size = 3) +
  scale_color_gradientn(colors = col_palette) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

Make pretty with palette Revolucion but now the colors depend on the position on the path:

col_palette <- mex.brewer("Revolucion")

paths_ex3 <- paths_ex3 %>%
  mutate(p_grid = (x * y)^2,
         size = 1 / (abs(step - l_path/2) + 1))

ggplot() + 
  geom_rect(aes(xmin = n_min, 
                xmax = n_max, 
                ymin = n_min, 
                ymax = n_max),
            fill = "black") +
  geom_circle(data = paths_ex3,
              aes(x0 = x,
                  y0 = y,
                  fill = step - l_path/2,
                  color = step - l_path/2,
                  r = 1.2 * size * res)) +
  geom_point(data = paths_ex3,
             aes(x = x - 1.2 * size * res * pi/4,
                 y = y + 1.2 * size * res * pi/4),
             color = col_palette[4],
             size = 0.1) +
  geom_circle(aes(x0 = 0,
                  y0 = 0,
                  r = 4),
              color = col_palette[4],
              fill = "black",
              size = 2) +
  scale_color_gradientn(colors = col_palette) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

Change the resolution:

# Set seed for reproducibility
#set.seed(346363)

# Parameters for creating the paths
res <- 3 # Resolution for the path, smaller numbers create more points per path/smoother paths
n_iter <- 20 # Number of iterations per path
n_path <- 5000 # Number of paths

# Sample starting points
st_point <- data.frame(x = runif(n_path, n_min, n_max),
                       #x = 2,
                       y = runif(n_path, n_min, n_max))

# Initialize data frame with paths
paths <- data.frame(x = numeric(), y = numeric(), step = numeric(), path = numeric())

for(p in 1:n_path){
  paths <- rbind(paths,
                 data.frame(st_point[p,], step = 1, path = p))
  next_x <- st_point[p, "x"]
  next_y <- st_point[p, "y"]
  for(i in 1:n_iter){
    # With the next point, retrieve the angle of the closest point in the direction of the flow
    angle <- df %>% 
      mutate(d = sqrt((x - next_x)^2 + (y - next_y)^2)) %>%
      filter(d == min(d)) %>%
      pull(angle)
    # Calculate the coordinates of the new next point and bind to paths
    paths <- rbind(paths,
                   data.frame(x = next_x + sign(next_x) * res * cos(angle), # Important to multiply by the sign of the next point
                              y = next_y + sign(next_y) * res * sin(angle), # Important to multiply by the sign of the next point
                              step = i + 1,
                              path = p))
    # Find the next starting point for the search
    next_x <- slice_tail(paths) %>%
      pull(x)
    next_y <- slice_tail(paths) %>%
      pull(y)
    
    # Check the boundary condition
    boundary_condition <- next_x < n_min | next_x >= n_max |
      next_y < n_min | next_y >= n_max
    # If at the boundary set i to n_iter to complete the loop
    if (boundary_condition) break
  }
}

# Rename paths

paths_ex4 <- paths

Plot paths:

Experiments with angle and resolutions

# Size of grid
n_min <- -17 
n_max <- 18
# Length of segment
l <- sqrt(2) + 0.1

df <- data.frame(expand.grid(x = seq(n_min, n_max, 1), y = seq(n_min, n_max, 1)))

Create new flow field:

# Offsets
x_o <- -15
y_o <- 5
init_r <- 0
d_angle <- 2

# remove the valence
df <- df %>%
  mutate(angle = (((x - x_o) * (y - y_o)) /(n_max - n_min)^2 * d_angle * pi + init_r * pi),# * sign(x * y), # add the signs
         xend = (x + l * cos(angle)),
         yend = (y + l * sin(angle)))

df %>%
  ggplot() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend)) +
  geom_point(aes(x = xend, 
                 y =yend)) +
  coord_equal() +
  theme_void()

Add obstacles:

df <- df %>%
  mutate(r1 = sqrt((x - 8)^2 + (y - 8)^2),
         r2 = sqrt((x + 8)^2 + (y + 8)^2)) %>%
  filter(r1 > 4,
         r2 > 4)

df %>%
  ggplot() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend)) +
  geom_point(aes(x = xend, 
                 y =yend)) +
  coord_equal() +
  theme_void()

Change the resolution:

# Set seed for reproducibility
#set.seed(346363)

# Parameters for creating the paths
res <- 3 # Resolution for the path, smaller numbers create more points per path/smoother paths
n_iter <- 50 # Number of iterations per path
n_path <- 5000 # Number of paths

# Sample starting points
st_point <- data.frame(x = runif(n_path, n_min, n_max),
                       #x = 2,
                       y = runif(n_path, n_min, n_max)) %>%
  mutate(r1 = sqrt((x - 8)^2 + (y - 8)^2),
         r2 = sqrt((x + 8)^2 + (y + 8)^2)) %>%
  filter(r1 > 4,
         r2 > 4) %>%
  select(-starts_with("r"))

# Update number of paths to account for the starting poins eliminated due to obstacles
n_path <- nrow(st_point)

# Initialize data frame with paths
paths <- data.frame(x = numeric(), y = numeric(), step = numeric(), path = numeric())

for(p in 1:n_path){
  paths <- rbind(paths,
                 data.frame(st_point[p,], step = 1, path = p))
  next_x <- st_point[p, "x"]
  next_y <- st_point[p, "y"]
  for(i in 1:n_iter){
    # With the next point, retrieve the angle of the closest point in the direction of the flow
    angle <- df %>% 
      mutate(d = sqrt((x - next_x)^2 + (y - next_y)^2)) %>%
      filter(d == min(d))
    min_d <- angle %>% pull(d)
    angle <- angle %>% pull(angle)
    # Calculate the coordinates of the new next point and bind to paths
    paths <- rbind(paths,
                   data.frame(x = next_x + res * cos(angle), # Removed the valence
                              y = next_y + res * sin(angle), # Removed the valence
                              step = i + 1,
                              path = p))
    # Find the next starting point for the search
    next_x <- slice_tail(paths) %>%
      pull(x)
    next_y <- slice_tail(paths) %>%
      pull(y)
    
    # Check the boundary condition
    boundary_condition <- next_x < n_min | next_x >= n_max |
      next_y < n_min | next_y >= n_max | min_d > sqrt(2) + 0.1
    # If at the boundary set i to n_iter to complete the loop
    if (boundary_condition) break
  }
}

# Rename paths

paths_ex5 <- paths

Plot paths:

# Add length of path to table:
paths_ex5 <- paths_ex5 %>% 
  left_join(paths_ex5 %>%
              group_by(path) %>%
              summarize(l_path = n(), # length of path
                        .groups = "drop"),
            by = "path") 

ggplot() + 
  geom_path(data = paths_ex5,
            aes(x = x,
                y = y,
                group = path),
            color = "blue",
            size = 1) +
  geom_segment(data = df,
               aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend)) +
  geom_point(data = df,
             aes(x = xend, 
                 y =yend)) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

Obstacle avoidance not working great, wonder why.

Prettify:

col_palette <- mex.brewer("Revolucion")

paths_ex5 <- paths_ex5 %>%
  mutate(p_grid = ((x - n_min) * (y - n_min ))^2,
         size = 1 / (abs(step - l_path/2) + 1))

plot1 <- ggplot() + 
  geom_rect(aes(xmin = n_min, 
                xmax = n_max, 
                ymin = n_min, 
                ymax = n_max),
            fill = "black") +
  geom_path(data = paths_ex5 %>%
              filter(x > n_min & x < n_max,
                     y > n_min & y < n_max),
              aes(x = x,
                  y = y,
                  group = path,
                  color = p_grid^(1/3))) +
  geom_circle(aes(x0 = -8,
                  y0 = -8,
                  r = 4),
              color = col_palette[4],
              fill = col_palette[8],
              size = 3) +
   geom_circle(aes(x0 = 8,
                   y0 = 8,
                   r = 4),
               color = col_palette[4],
               fill = col_palette[1],
               size = 3) +
  geom_rect(aes(xmin = n_min, 
                xmax = n_max, 
                ymin = n_min, 
                ymax = n_max),
            fill = NA,
            color = col_palette[8], 
            size = 3) +
  scale_color_gradientn(colors = col_palette) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

plot1

col_palette <- mex.brewer("Atentado")

paths_ex5 <- paths_ex5 %>%
  mutate(p_grid = ((x - n_min) * (y - n_min ))^2,
         size = 1 / (abs(step - l_path/2) + 1))

plot2 <- ggplot() + 
  geom_rect(aes(xmin = -n_min, 
                xmax = -n_max, 
                ymin = n_min, 
                ymax = n_max),
            fill = "black") +
  geom_path(data = paths_ex5 %>%
              filter(x > n_min & x < n_max,
                     y > n_min & y < n_max),
              aes(x = -x,
                  y = y,
                  group = path,
                  color = p_grid^(1/3))) +
  geom_circle(aes(x0 = 8,
                  y0 = -8,
                  r = 4),
              color = col_palette[4],
              fill = col_palette[8],
              size = 3) +
   geom_circle(aes(x0 = -8,
                   y0 = 8,
                   r = 4),
               color = col_palette[4],
               fill = col_palette[1],
               size = 3) +
  geom_rect(aes(xmin = -n_min, 
                xmax = -n_max, 
                ymin = n_min, 
                ymax = n_max),
            fill = NA,
            color = col_palette[8], 
            size = 3) +
  scale_color_gradientn(colors = col_palette) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

plot2

col_palette <- mex.brewer("Alacena")

paths_ex5 <- paths_ex5 %>%
  mutate(p_grid = ((x - n_min) * (y - n_min ))^2,
         size = 1 / (abs(step - l_path/2) + 1))

plot3 <- ggplot() + 
  geom_rect(aes(xmin = n_min, 
                xmax = n_max, 
                ymin = -n_min, 
                ymax = -n_max),
            fill = "black") +
  geom_path(data = paths_ex5 %>%
              filter(x > n_min & x < n_max,
                     y > n_min & y < n_max),
              aes(x = x,
                  y = -y,
                  group = path,
                  color = p_grid^(1/3))) +
  geom_circle(aes(x0 = 8,
                  y0 = -8,
                  r = 4),
              color = col_palette[4],
              fill = col_palette[8],
              size = 3) +
   geom_circle(aes(x0 = -8,
                   y0 = 8,
                   r = 4),
               color = col_palette[4],
               fill = col_palette[1],
               size = 3) +
  geom_rect(aes(xmin = n_min, 
                xmax = n_max, 
                ymin = -n_min, 
                ymax = -n_max),
            fill = NA,
            color = col_palette[8], 
            size = 3) +
  scale_color_gradientn(colors = col_palette) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

plot3

col_palette <- mex.brewer("Ronda")

paths_ex5 <- paths_ex5 %>%
  mutate(p_grid = ((x - n_min) * (y - n_min ))^2,
         size = 1 / (abs(step - l_path/2) + 1))

plot4 <- ggplot() + 
  geom_rect(aes(xmin = -n_min, 
                xmax = -n_max, 
                ymin = -n_min, 
                ymax = -n_max),
            fill = "black") +
  geom_path(data = paths_ex5 %>%
              filter(x > n_min & x < n_max,
                     y > n_min & y < n_max),
              aes(x = -x,
                  y = -y,
                  group = path,
                  color = p_grid^(1/3))) +
  geom_circle(aes(x0 = -8,
                  y0 = -8,
                  r = 4),
              color = col_palette[4],
              fill = col_palette[8],
              size = 3) +
   geom_circle(aes(x0 = 8,
                   y0 = 8,
                   r = 4),
               color = col_palette[4],
               fill = col_palette[1],
               size = 3) +
  geom_rect(aes(xmin = -n_min, 
                xmax = -n_max, 
                ymin = -n_min, 
                ymax = -n_max),
            fill = NA,
            color = col_palette[8], 
            size = 3) +
  scale_color_gradientn(colors = col_palette) +
  coord_equal() +
  theme_void() + 
  theme(legend.position = "none")

plot4

LS0tCnRpdGxlOiAiRXhwZXJpbWVudHMgd2l0aCBGaWRlbnphIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpNeSBiYXNpYyBjb2RlIGltcGxlbWVudGluZyB0aGUgRmlkZW56YSBhbGdvcml0aG0gaXMgaW4gdGhlIFJFQURNRS5SbWQgZmlsZSBpbiBgMDQtZmlkZW56YWAuCgpIZXJlIEkgd2FudCB0byBwbGF5IHdpdGggaXQgYSBiaXQgbW9yZS4KCkZvciBleGFtcGxlLCBJIHVzZWQgdGhlIGxlbmd0aCBvZiB0aGUgcGF0aCBidXQgY291bGQgdXNlIHRoZSBzZXF1ZW5jZSBvZiBzdGVwcyB0byBtb2RpZnkgc29tZSBhZXN0aGV0aWMuCgpJIHdpbGwgdXNlIHRoZSBmb2xsb3dpbmcgcGFja2FnZXM6CmBgYHtyIGxvYWQtcGFja2FnZXMsIGNhY2hlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZ2dmb3JjZSkKbGlicmFyeShNZXhCcmV3ZXIpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKClRoaXMgYXJlIHRoZSBiYXNpYyBzdGVwczoKCjEuIEdlbmVyYXRlIGEgZ3JpZAotIEltcG9ydGFudCBwYXJhbWV0ZXJzOiBzaXplIG9mIHRoZSBncmlkIGFuZCB2YWxlbmNlIG9mIHRoZSBjb29yZGluYXRlcyAocG9zaXRpdmUvbmVnYXRpdmUpCgoyLiBHZW5lcmF0ZSBhIGZsb3cgZmllbGQgb24gdGhlIGdyaWQKLSBJbXBvcnRhbnQgcGFyYW1ldGVyczogb2Zmc2V0IGFuZCBmb3JtdWxhcyBmb3IgdGhlIGFuZ2xlcyBvZiB0aGUgZmxvdyBmaWVsZAoKMy4gR2VuZXJhdGUgcGF0aHMKLSBJbXBvcnRhbnQgcGFyYW1ldGVyczogcmVzb2x1dGlvbiwgbnVtYmVyIG9mIGl0ZXJhdGlvbnMgKGxlbmd0aCBvZiBwYXRoKSwgbnVtYmVyIG9mIHBhdGhzIAoKIyMgR2VuZXJhdGUgR3JpZAoKYGBge3J9CiMgU2l6ZSBvZiBncmlkCm5fbWluIDwtIC0xNyAKbl9tYXggPC0gMTgKIyBMZW5ndGggb2Ygc2VnbWVudApsIDwtIHNxcnQoMikgKyAwLjEKCmRmIDwtIGRhdGEuZnJhbWUoZXhwYW5kLmdyaWQoeCA9IHNlcShuX21pbiwgbl9tYXgsIDEpLCB5ID0gc2VxKG5fbWluLCBuX21heCwgMSkpKQpgYGAKCkNyZWF0ZSBhbmQgcGxvdCB0aGUgZmxvdyBmaWVsZDoKYGBge3J9CiMgT2Zmc2V0cwp4X28gPC0gLTAKeV9vIDwtIC0wCgpkZiA8LSBkZiAlPiUKICBtdXRhdGUoYW5nbGUgPSAoKHggLSB4X28pICogKHkgLSB5X28pKSAvKG5fbWF4IC0gbl9taW4pXjIgKiAyICogcGksCiAgICAgICAgIHhlbmQgPSAoeCArIGwgKiBjb3MoYW5nbGUpKSwKICAgICAgICAgeWVuZCA9ICh5ICsgbCAqIHNpbihhbmdsZSkpKQoKZGYgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHgsCiAgICAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgICAgIHhlbmQgPSB4ZW5kLAogICAgICAgICAgICAgICAgICAgeWVuZCA9IHllbmQpKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHhlbmQsIAogICAgICAgICAgICAgICAgIHkgPXllbmQpKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfdm9pZCgpCmBgYAoKQW5vdGhlciBwb3NzaWJsZSBwYXJhbWV0ZXIgaXMgdXNpbmcgdGhlIHZhbGVuY2Ugb2YgdGhlIGNvb3JkaW5hdGVzIHRvIGFsdGVyIHRoZSBhbmdsZToKYGBge3J9CiMgT2Zmc2V0cwp4X28gPC0gLTAKeV9vIDwtIC0wCgpkZiA8LSBkZiAlPiUKICBtdXRhdGUoYW5nbGUgPSAoKHggLSB4X28pICogKHkgLSB5X28pKSAvKG5fbWF4IC0gbl9taW4pXjIgKiAyICogcGkgKiBzaWduKHggKiB5KSwgIyBhZGQgdGhlIHNpZ25zCiAgICAgICAgIHhlbmQgPSAoeCArIGwgKiBjb3MoYW5nbGUpKSwKICAgICAgICAgeWVuZCA9ICh5ICsgbCAqIHNpbihhbmdsZSkpKQoKZGYgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHgsCiAgICAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgICAgIHhlbmQgPSB4ZW5kLAogICAgICAgICAgICAgICAgICAgeWVuZCA9IHllbmQpKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHhlbmQsIAogICAgICAgICAgICAgICAgIHkgPXllbmQpKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfdm9pZCgpCmBgYAoKVGhlIHZhbGVuY2UgY291bGQgYmUgdXNlZCB0byBjaGFuZ2UgdGhlIGRpcmVjdGlvbiBvZiB0aGUgZGlzcGxhY2VtZW50IG9mIHRoZSBjb29yZGluYXRlLCBpbnN0ZWFkIG9mIHRoZSBhbmdsZToKYGBge3J9CiMgT2Zmc2V0cwp4X28gPC0gLTAKeV9vIDwtIC0wCgpkZiA8LSBkZiAlPiUKICBtdXRhdGUoYW5nbGUgPSAoKHggLSB4X28pICogKHkgLSB5X28pKSAvKG5fbWF4IC0gbl9taW4pXjIgKiAyICogcGksICMgYWRkIHRoZSBzaWducwogICAgICAgICB4ZW5kID0gKHggKyBzaWduKHgpICogbCAqIGNvcyhhbmdsZSkpLAogICAgICAgICB5ZW5kID0gKHkgKyBzaWduKHkpICogbCAqIHNpbihhbmdsZSkpKQoKZGYgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHgsCiAgICAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgICAgIHhlbmQgPSB4ZW5kLAogICAgICAgICAgICAgICAgICAgeWVuZCA9IHllbmQpKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHhlbmQsIAogICAgICAgICAgICAgICAgIHkgPXllbmQpKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfdm9pZCgpCmBgYAoKQW5vdGhlciBwb3NzaWJsZSBwYXJhbWV0ZXIgaXMgYW4gaW5pdGlhbCByb3RhdGlvbjoKYGBge3J9CiMgT2Zmc2V0cwp4X28gPC0gLTAKeV9vIDwtIC0wCmluaXRfciA8LSAxLzgKCmRmIDwtIGRmICU+JQogIG11dGF0ZShhbmdsZSA9ICgoeCAtIHhfbykgKiAoeSAtIHlfbykpIC8obl9tYXggLSBuX21pbileMiAqIDIgKiBwaSArIGluaXRfciAqIHBpLCAjIGFkZCB0aGUgc2lnbnMKICAgICAgICAgeGVuZCA9ICh4ICsgbCAqIGNvcyhhbmdsZSkpLAogICAgICAgICB5ZW5kID0gKHkgKyBsICogc2luKGFuZ2xlKSkpCgpkZiAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwKICAgICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgICAgeGVuZCA9IHhlbmQsCiAgICAgICAgICAgICAgICAgICB5ZW5kID0geWVuZCkpICsKICBnZW9tX3BvaW50KGFlcyh4ID0geGVuZCwgCiAgICAgICAgICAgICAgICAgeSA9eWVuZCkpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkKYGBgCgpUaGlzIG9uZSBoYXMgYW4gaW5pdGlhbCByb3RhdGlvbiBhbmQgdmFsZW5jZSBmb3IgdGhlIGFuZ2xlOgpgYGB7cn0KIyBPZmZzZXRzCnhfbyA8LSAtMAp5X28gPC0gLTAKaW5pdF9yIDwtIDEvOAoKZGYgPC0gZGYgJT4lCiAgbXV0YXRlKGFuZ2xlID0gKCgoeCAtIHhfbykgKiAoeSAtIHlfbykpIC8obl9tYXggLSBuX21pbileMiAqIDIgKiBwaSArIGluaXRfciAqIHBpKSAqIHNpZ24oeCAqIHkpLCAjIGFkZCB0aGUgc2lnbnMKICAgICAgICAgeGVuZCA9ICh4ICsgbCAqIGNvcyhhbmdsZSkpLAogICAgICAgICB5ZW5kID0gKHkgKyBsICogc2luKGFuZ2xlKSkpCgpkZiAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwKICAgICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgICAgeGVuZCA9IHhlbmQsCiAgICAgICAgICAgICAgICAgICB5ZW5kID0geWVuZCkpICsKICBnZW9tX3BvaW50KGFlcyh4ID0geGVuZCwgCiAgICAgICAgICAgICAgICAgeSA9eWVuZCkpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkKYGBgCgpUaGlzIG9uZSBoYXMgYW4gaW5pdGlhbCByb3RhdGlvbiBhbmQgdmFsZW5jZSBmb3IgdGhlIGRpc3BsYWNlbWVudDoKYGBge3J9CiMgT2Zmc2V0cwp4X28gPC0gLTAKeV9vIDwtIC0wCmluaXRfciA8LSAxLzgKCmRmIDwtIGRmICU+JQogIG11dGF0ZShhbmdsZSA9ICgoeCAtIHhfbykgKiAoeSAtIHlfbykpIC8obl9tYXggLSBuX21pbileMiAqIDIgKiBwaSArIGluaXRfciAqIHBpLCAjIGFkZCB0aGUgc2lnbnMKICAgICAgICAgeGVuZCA9ICh4ICsgc2lnbih4KSAqIGwgKiBjb3MoYW5nbGUpKSwKICAgICAgICAgeWVuZCA9ICh5ICsgc2lnbih5KSAqIGwgKiBzaW4oYW5nbGUpKSkKCmRmICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSB4LAogICAgICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICAgICB4ZW5kID0geGVuZCwKICAgICAgICAgICAgICAgICAgIHllbmQgPSB5ZW5kKSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSB4ZW5kLCAKICAgICAgICAgICAgICAgICB5ID15ZW5kKSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lX3ZvaWQoKQpgYGAKClRoaXMgb25lIGhhcyBhbiBpbml0aWFsIHJvdGF0aW9uLCB2YWxlbmNlIGZvciB0aGUgZGlzcGxhY2VtZW50IF9hbmRfIHZhbGVuY2UgZm9yIHRoZSBhbmdsZToKYGBge3J9CiMgT2Zmc2V0cwp4X28gPC0gLTAKeV9vIDwtIC0wCmluaXRfciA8LSAxLzgKCmRmIDwtIGRmICU+JQogIG11dGF0ZShhbmdsZSA9ICgoKHggLSB4X28pICogKHkgLSB5X28pKSAvKG5fbWF4IC0gbl9taW4pXjIgKiAyICogcGkgKyBpbml0X3IgKiBwaSkgKiBzaWduKHggKiB5KSwgIyBhZGQgdGhlIHNpZ25zCiAgICAgICAgIHhlbmQgPSAoeCArIHNpZ24oeCkgKiBsICogY29zKGFuZ2xlKSksCiAgICAgICAgIHllbmQgPSAoeSArIHNpZ24oeSkgKiBsICogc2luKGFuZ2xlKSkpCgpkZiAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwKICAgICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgICAgeGVuZCA9IHhlbmQsCiAgICAgICAgICAgICAgICAgICB5ZW5kID0geWVuZCkpICsKICBnZW9tX3BvaW50KGFlcyh4ID0geGVuZCwgCiAgICAgICAgICAgICAgICAgeSA9eWVuZCkpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkKYGBgCgpNYWtlIHBhdGhzIHVzaW5nIHRoZSBsYXN0IGZsb3cgZmllbGQ6CmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKI3NldC5zZWVkKDM0NjM2MykKCiMgUGFyYW1ldGVycyBmb3IgY3JlYXRpbmcgdGhlIHBhdGhzCnJlcyA8LSAwLjIgIyBSZXNvbHV0aW9uIGZvciB0aGUgcGF0aCwgc21hbGxlciBudW1iZXJzIGNyZWF0ZSBtb3JlIHBvaW50cyBwZXIgcGF0aC9zbW9vdGhlciBwYXRocwpuX2l0ZXIgPC0gMjAgIyBOdW1iZXIgb2YgaXRlcmF0aW9ucyBwZXIgcGF0aApuX3BhdGggPC0gNTAwICMgTnVtYmVyIG9mIHBhdGhzCgojIFNhbXBsZSBzdGFydGluZyBwb2ludHMKc3RfcG9pbnQgPC0gZGF0YS5mcmFtZSh4ID0gcnVuaWYobl9wYXRoLCBuX21pbiwgbl9tYXgpLAogICAgICAgICAgICAgICAgICAgICAgICN4ID0gMiwKICAgICAgICAgICAgICAgICAgICAgICB5ID0gcnVuaWYobl9wYXRoLCBuX21pbiwgbl9tYXgpKQoKIyBJbml0aWFsaXplIGRhdGEgZnJhbWUgd2l0aCBwYXRocwpwYXRocyA8LSBkYXRhLmZyYW1lKHggPSBudW1lcmljKCksIHkgPSBudW1lcmljKCksIHN0ZXAgPSBudW1lcmljKCksIHBhdGggPSBudW1lcmljKCkpCgpmb3IocCBpbiAxOm5fcGF0aCl7CiAgcGF0aHMgPC0gcmJpbmQocGF0aHMsCiAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZShzdF9wb2ludFtwLF0sIHN0ZXAgPSAxLCBwYXRoID0gcCkpCiAgbmV4dF94IDwtIHN0X3BvaW50W3AsICJ4Il0KICBuZXh0X3kgPC0gc3RfcG9pbnRbcCwgInkiXQogIGZvcihpIGluIDE6bl9pdGVyKXsKICAgICMgV2l0aCB0aGUgbmV4dCBwb2ludCwgcmV0cmlldmUgdGhlIGFuZ2xlIG9mIHRoZSBjbG9zZXN0IHBvaW50IGluIHRoZSBkaXJlY3Rpb24gb2YgdGhlIGZsb3cKICAgIGFuZ2xlIDwtIGRmICU+JSAKICAgICAgbXV0YXRlKGQgPSBzcXJ0KCh4IC0gbmV4dF94KV4yICsgKHkgLSBuZXh0X3kpXjIpKSAlPiUKICAgICAgZmlsdGVyKGQgPT0gbWluKGQpKSAlPiUKICAgICAgcHVsbChhbmdsZSkKICAgICMgQ2FsY3VsYXRlIHRoZSBjb29yZGluYXRlcyBvZiB0aGUgbmV3IG5leHQgcG9pbnQgYW5kIGJpbmQgdG8gcGF0aHMKICAgIHBhdGhzIDwtIHJiaW5kKHBhdGhzLAogICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZSh4ID0gbmV4dF94ICsgc2lnbihuZXh0X3gpICogcmVzICogY29zKGFuZ2xlKSwgIyBJbXBvcnRhbnQgdG8gbXVsdGlwbHkgYnkgdGhlIHNpZ24gb2YgdGhlIG5leHQgcG9pbnQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IG5leHRfeSArIHNpZ24obmV4dF95KSAqIHJlcyAqIHNpbihhbmdsZSksICMgSW1wb3J0YW50IHRvIG11bHRpcGx5IGJ5IHRoZSBzaWduIG9mIHRoZSBuZXh0IHBvaW50CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXAgPSBpICsgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0aCA9IHApKQogICAgIyBGaW5kIHRoZSBuZXh0IHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgc2VhcmNoCiAgICBuZXh0X3ggPC0gc2xpY2VfdGFpbChwYXRocykgJT4lCiAgICAgIHB1bGwoeCkKICAgIG5leHRfeSA8LSBzbGljZV90YWlsKHBhdGhzKSAlPiUKICAgICAgcHVsbCh5KQogICAgCiAgICAjIENoZWNrIHRoZSBib3VuZGFyeSBjb25kaXRpb24KICAgIGJvdW5kYXJ5X2NvbmRpdGlvbiA8LSBuZXh0X3ggPCBuX21pbiB8IG5leHRfeCA+PSBuX21heCB8CiAgICAgIG5leHRfeSA8IG5fbWluIHwgbmV4dF95ID49IG5fbWF4CiAgICAjIElmIGF0IHRoZSBib3VuZGFyeSBzZXQgaSB0byBuX2l0ZXIgdG8gY29tcGxldGUgdGhlIGxvb3AKICAgIGlmIChib3VuZGFyeV9jb25kaXRpb24pIGJyZWFrCiAgfQp9CgojIFJlbmFtZSBwYXRocwoKcGF0aHNfZXgzIDwtIHBhdGhzCmBgYAoKUGxvdCBwYXRoczoKYGBge3IgcGF0aHMtZXgzfQpnZ3Bsb3QoKSArIAogIGdlb21fbGluZShkYXRhID0gcGF0aHNfZXgzLAogICAgICAgICAgICBhZXMoeCA9IHgsCiAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgIGdyb3VwID0gcGF0aCksCiAgICAgICAgICAgIGNvbG9yID0gImJsdWUiLAogICAgICAgICAgICBzaXplID0gMSkgKwogIGdlb21fc2VnbWVudChkYXRhID0gZGYsCiAgICAgICAgICAgICAgIGFlcyh4ID0geCwKICAgICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgICAgeGVuZCA9IHhlbmQsCiAgICAgICAgICAgICAgICAgICB5ZW5kID0geWVuZCkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkZiwKICAgICAgICAgICAgIGFlcyh4ID0geGVuZCwgCiAgICAgICAgICAgICAgICAgeSA9eWVuZCkpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKSW5zdGVhZCBvZiBsaW5lcyB1c2Ugc3ltYm9sczoKYGBge3J9CmdncGxvdCgpICsgCiAgZ2VvbV9wb2ludChkYXRhID0gcGF0aHNfZXgzICU+JQogICAgICAgICAgICAgICBtdXRhdGUocF9ncmlkID0gKHggKiB5KV4yKSwKICAgICAgICAgICAgIGFlcyh4ID0geCwKICAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgICBncm91cCA9IHBhdGgsIAogICAgICAgICAgICAgICAgIGNvbG9yID1wX2dyaWQpLAogICAgICAgICAgICAgc2l6ZSA9IDEpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKQWRkIGxlbmd0aCBvZiBwYXRoIHRvIHRhYmxlOgpgYGB7cn0KcGF0aHNfZXgzIDwtIHBhdGhzX2V4MyAlPiUgCiAgbGVmdF9qb2luKHBhdGhzX2V4MyAlPiUKICAgICAgICAgICAgICBncm91cF9ieShwYXRoKSAlPiUKICAgICAgICAgICAgICBzdW1tYXJpemUobF9wYXRoID0gbigpLCAjIGxlbmd0aCBvZiBwYXRoCiAgICAgICAgICAgICAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpLAogICAgICAgICAgICBieSA9ICJwYXRoIikgCmBgYAoKQ2hhbmdlIHRoZSBzaXplIG9mIHRoZSBzeW1ib2xzIGJhc2VkIG9uIHRoZWlyIHBvc2l0aW9uIGluIHRoZSBwYXRoOgpgYGB7cn0KcGF0aHNfZXgzIDwtIHBhdGhzX2V4MyAlPiUKICBtdXRhdGUocF9ncmlkID0gKHggKiB5KV4yLAogICAgICAgICBzaXplID0gMSAvIChhYnMoc3RlcCAtIGxfcGF0aC8yKSArIDEpKQoKZ2dwbG90KGRhdGEgPSBwYXRoc19leDMpICsgCiAgZ2VvbV9jaXJjbGUoYWVzKHgwID0geCwKICAgICAgICAgICAgICAgICAgeTAgPSB5LCAKICAgICAgICAgICAgICAgICAgZmlsbCA9cF9ncmlkLAogICAgICAgICAgICAgICAgICBjb2xvciA9IHBfZ3JpZCwKICAgICAgICAgICAgICAgICAgciA9IHNpemUgKiByZXMpLAogICAgICAgICAgICAgIHNpemUgPSAxKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHggLSAwLjcgKiBzaXplICogcmVzICogcGkvNCwKICAgICAgICAgICAgICAgICB5ID0geSArIDAuNyAqIHNpemUgKiByZXMgKiBwaS80KSwKICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwgCiAgICAgICAgICAgICBzaXplID0gMC4xKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfdm9pZCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCk1ha2UgcHJldHR5IHdpdGggcGFsZXR0ZSBgUm9uZGFgOgpgYGB7cn0KY29sX3BhbGV0dGUgPC0gbWV4LmJyZXdlcigiUm9uZGEiKQoKcGF0aHNfZXgzIDwtIHBhdGhzX2V4MyAlPiUKICBtdXRhdGUocF9ncmlkID0gKHggKiB5KV4yLAogICAgICAgICBzaXplID0gMSAvIChhYnMoc3RlcCAtIGxfcGF0aC8yKSArIDEpKQoKZ2dwbG90KCkgKyAKICBnZW9tX3JlY3QoYWVzKHhtaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB4bWF4ID0gbl9tYXgsIAogICAgICAgICAgICAgICAgeW1pbiA9IG5fbWluLCAKICAgICAgICAgICAgICAgIHltYXggPSBuX21heCksCiAgICAgICAgICAgIGZpbGwgPSAiYmxhY2siKSArCiAgZ2VvbV9jaXJjbGUoZGF0YSA9IHBhdGhzX2V4MywKICAgICAgICAgICAgICBhZXMoeDAgPSB4LAogICAgICAgICAgICAgICAgICB5MCA9IHksCiAgICAgICAgICAgICAgICAgIGZpbGwgPXBfZ3JpZCwKICAgICAgICAgICAgICAgICAgY29sb3IgPSBwX2dyaWQsCiAgICAgICAgICAgICAgICAgIHIgPSAxLjIgKiBzaXplICogcmVzKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHBhdGhzX2V4MywKICAgICAgICAgICAgIGFlcyh4ID0geCAtIDEuMiAqIHNpemUgKiByZXMgKiBwaS80LAogICAgICAgICAgICAgICAgIHkgPSB5ICsgMS4yICogc2l6ZSAqIHJlcyAqIHBpLzQpLAogICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVs0XSwKICAgICAgICAgICAgIHNpemUgPSAwLjEpICsKICBnZW9tX2NpcmNsZShhZXMoeDAgPSAwLAogICAgICAgICAgICAgICAgICB5MCA9IDAsCiAgICAgICAgICAgICAgICAgIHIgPSA0KSwKICAgICAgICAgICAgICBjb2xvciA9IGNvbF9wYWxldHRlWzRdLAogICAgICAgICAgICAgIGZpbGwgPSAiYmxhY2siLAogICAgICAgICAgICAgIHNpemUgPSAyKSArCiAgZ2VvbV9yZWN0KGFlcyh4bWluID0gbl9taW4sIAogICAgICAgICAgICAgICAgeG1heCA9IG5fbWF4LCAKICAgICAgICAgICAgICAgIHltaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB5bWF4ID0gbl9tYXgpLAogICAgICAgICAgICBmaWxsID0gTkEsCiAgICAgICAgICAgIGNvbG9yID0gY29sX3BhbGV0dGVbMTBdKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbF9wYWxldHRlKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfdm9pZCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCk1ha2UgcHJldHR5IHdpdGggcGFsZXR0ZSBgUmV2b2x1Y2lvbmAuIFRoZSBjb2xvciBkZXBlbmRzIG9uIHRoZSBwb3NpdGlvbiBpbiB0aGUgZ3JpZDoKYGBge3J9CmNvbF9wYWxldHRlIDwtIG1leC5icmV3ZXIoIlJldm9sdWNpb24iKQoKcGF0aHNfZXgzIDwtIHBhdGhzX2V4MyAlPiUKICBtdXRhdGUocF9ncmlkID0gKHggKiB5KV4yLAogICAgICAgICBzaXplID0gMSAvIChhYnMoc3RlcCAtIGxfcGF0aC8yKSArIDEpKQoKZ2dwbG90KCkgKyAKICBnZW9tX3JlY3QoYWVzKHhtaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB4bWF4ID0gbl9tYXgsIAogICAgICAgICAgICAgICAgeW1pbiA9IG5fbWluLCAKICAgICAgICAgICAgICAgIHltYXggPSBuX21heCksCiAgICAgICAgICAgIGZpbGwgPSAiYmxhY2siKSArCiAgZ2VvbV9jaXJjbGUoZGF0YSA9IHBhdGhzX2V4MywKICAgICAgICAgICAgICBhZXMoeDAgPSB4LAogICAgICAgICAgICAgICAgICB5MCA9IHksCiAgICAgICAgICAgICAgICAgIGZpbGwgPXBfZ3JpZCwKICAgICAgICAgICAgICAgICAgY29sb3IgPSBwX2dyaWQsCiAgICAgICAgICAgICAgICAgIHIgPSAxLjIgKiBzaXplICogcmVzKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHBhdGhzX2V4MywKICAgICAgICAgICAgIGFlcyh4ID0geCAtIDEuMiAqIHNpemUgKiByZXMgKiBwaS80LAogICAgICAgICAgICAgICAgIHkgPSB5ICsgMS4yICogc2l6ZSAqIHJlcyAqIHBpLzQpLAogICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVs0XSwKICAgICAgICAgICAgIHNpemUgPSAwLjEpICsKICBnZW9tX2NpcmNsZShhZXMoeDAgPSAwLAogICAgICAgICAgICAgICAgICB5MCA9IDAsCiAgICAgICAgICAgICAgICAgIHIgPSA0KSwKICAgICAgICAgICAgICBjb2xvciA9IGNvbF9wYWxldHRlWzRdLAogICAgICAgICAgICAgIGZpbGwgPSAiYmxhY2siLAogICAgICAgICAgICAgIHNpemUgPSAyKSArCiAgZ2VvbV9yZWN0KGFlcyh4bWluID0gbl9taW4sIAogICAgICAgICAgICAgICAgeG1heCA9IG5fbWF4LCAKICAgICAgICAgICAgICAgIHltaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB5bWF4ID0gbl9tYXgpLAogICAgICAgICAgICBmaWxsID0gTkEsCiAgICAgICAgICAgIGNvbG9yID0gY29sX3BhbGV0dGVbMTBdKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbF9wYWxldHRlKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfdm9pZCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCk1ha2UgcHJldHR5IHdpdGggcGFsZXR0ZSBgUmV2b2x1Y2lvbmAgYnV0IG5vdyBjaGFuZ2UgdGhlIHJhdGUgb2YgY2hhbmdlIG9mIHRoZSBjb2xvcnM6CmBgYHtyfQpjb2xfcGFsZXR0ZSA8LSBtZXguYnJld2VyKCJSZXZvbHVjaW9uIikKCnBhdGhzX2V4MyA8LSBwYXRoc19leDMgJT4lCiAgbXV0YXRlKHBfZ3JpZCA9ICh4ICogeSleMiwKICAgICAgICAgc2l6ZSA9IDEgLyAoYWJzKHN0ZXAgLSBsX3BhdGgvMikgKyAxKSkKCmdncGxvdCgpICsgCiAgZ2VvbV9yZWN0KGFlcyh4bWluID0gbl9taW4sIAogICAgICAgICAgICAgICAgeG1heCA9IG5fbWF4LCAKICAgICAgICAgICAgICAgIHltaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB5bWF4ID0gbl9tYXgpLAogICAgICAgICAgICBmaWxsID0gImJsYWNrIikgKwogIGdlb21fY2lyY2xlKGRhdGEgPSBwYXRoc19leDMsCiAgICAgICAgICAgICAgYWVzKHgwID0geCwKICAgICAgICAgICAgICAgICAgeTAgPSB5LAogICAgICAgICAgICAgICAgICBmaWxsID0gcF9ncmlkXigxLzMpLAogICAgICAgICAgICAgICAgICBjb2xvciA9IHBfZ3JpZF4oMS8zKSwKICAgICAgICAgICAgICAgICAgciA9IDEuMiAqIHNpemUgKiByZXMpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcGF0aHNfZXgzLAogICAgICAgICAgICAgYWVzKHggPSB4IC0gMS4yICogc2l6ZSAqIHJlcyAqIHBpLzQsCiAgICAgICAgICAgICAgICAgeSA9IHkgKyAxLjIgKiBzaXplICogcmVzICogcGkvNCksCiAgICAgICAgICAgICBjb2xvciA9IGNvbF9wYWxldHRlWzRdLAogICAgICAgICAgICAgc2l6ZSA9IDAuMSkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IG5fbWluLCB4ZW5kID0gbl9tYXgsIHkgPSAwLCB5ZW5kID0gMCksCiAgICAgICAgICAgICBjb2xvciA9IGNvbF9wYWxldHRlWzRdLAogICAgICAgICAgICAgc2l6ZSA9IDMpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gMCwgeSA9IG5fbWluLCB5ZW5kID0gbl9tYXgpLAogICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVs0XSwKICAgICAgICAgICAgIHNpemUgPSAzKSArCiAgZ2VvbV9jaXJjbGUoYWVzKHgwID0gMCwKICAgICAgICAgICAgICAgICAgeTAgPSAwLAogICAgICAgICAgICAgICAgICByID0gNCksCiAgICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVs0XSwKICAgICAgICAgICAgICBmaWxsID0gImJsYWNrIiwKICAgICAgICAgICAgICBzaXplID0gMykgKwogIGdlb21fcmVjdChhZXMoeG1pbiA9IG5fbWluLCAKICAgICAgICAgICAgICAgIHhtYXggPSBuX21heCwgCiAgICAgICAgICAgICAgICB5bWluID0gbl9taW4sIAogICAgICAgICAgICAgICAgeW1heCA9IG5fbWF4KSwKICAgICAgICAgICAgZmlsbCA9IE5BLAogICAgICAgICAgICBjb2xvciA9IGNvbF9wYWxldHRlWzRdLCAKICAgICAgICAgICAgc2l6ZSA9IDMpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gY29sX3BhbGV0dGUpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpgYGAKCk1ha2UgcHJldHR5IHdpdGggcGFsZXR0ZSBgUmV2b2x1Y2lvbmAgYnV0IG5vdyB0aGUgY29sb3JzIGRlcGVuZCBvbiB0aGUgcG9zaXRpb24gb24gdGhlIHBhdGg6CmBgYHtyfQpjb2xfcGFsZXR0ZSA8LSBtZXguYnJld2VyKCJSZXZvbHVjaW9uIikKCnBhdGhzX2V4MyA8LSBwYXRoc19leDMgJT4lCiAgbXV0YXRlKHBfZ3JpZCA9ICh4ICogeSleMiwKICAgICAgICAgc2l6ZSA9IDEgLyAoYWJzKHN0ZXAgLSBsX3BhdGgvMikgKyAxKSkKCmdncGxvdCgpICsgCiAgZ2VvbV9yZWN0KGFlcyh4bWluID0gbl9taW4sIAogICAgICAgICAgICAgICAgeG1heCA9IG5fbWF4LCAKICAgICAgICAgICAgICAgIHltaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB5bWF4ID0gbl9tYXgpLAogICAgICAgICAgICBmaWxsID0gImJsYWNrIikgKwogIGdlb21fY2lyY2xlKGRhdGEgPSBwYXRoc19leDMsCiAgICAgICAgICAgICAgYWVzKHgwID0geCwKICAgICAgICAgICAgICAgICAgeTAgPSB5LAogICAgICAgICAgICAgICAgICBmaWxsID0gc3RlcCAtIGxfcGF0aC8yLAogICAgICAgICAgICAgICAgICBjb2xvciA9IHN0ZXAgLSBsX3BhdGgvMiwKICAgICAgICAgICAgICAgICAgciA9IDEuMiAqIHNpemUgKiByZXMpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcGF0aHNfZXgzLAogICAgICAgICAgICAgYWVzKHggPSB4IC0gMS4yICogc2l6ZSAqIHJlcyAqIHBpLzQsCiAgICAgICAgICAgICAgICAgeSA9IHkgKyAxLjIgKiBzaXplICogcmVzICogcGkvNCksCiAgICAgICAgICAgICBjb2xvciA9IGNvbF9wYWxldHRlWzRdLAogICAgICAgICAgICAgc2l6ZSA9IDAuMSkgKwogIGdlb21fY2lyY2xlKGFlcyh4MCA9IDAsCiAgICAgICAgICAgICAgICAgIHkwID0gMCwKICAgICAgICAgICAgICAgICAgciA9IDQpLAogICAgICAgICAgICAgIGNvbG9yID0gY29sX3BhbGV0dGVbNF0sCiAgICAgICAgICAgICAgZmlsbCA9ICJibGFjayIsCiAgICAgICAgICAgICAgc2l6ZSA9IDIpICsKICBnZW9tX3JlY3QoYWVzKHhtaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB4bWF4ID0gbl9tYXgsIAogICAgICAgICAgICAgICAgeW1pbiA9IG5fbWluLCAKICAgICAgICAgICAgICAgIHltYXggPSBuX21heCksCiAgICAgICAgICAgIGZpbGwgPSBOQSwKICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVsxMF0pICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gY29sX3BhbGV0dGUpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKQ2hhbmdlIHRoZSByZXNvbHV0aW9uOgpgYGB7cn0KIyBTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CiNzZXQuc2VlZCgzNDYzNjMpCgojIFBhcmFtZXRlcnMgZm9yIGNyZWF0aW5nIHRoZSBwYXRocwpyZXMgPC0gMyAjIFJlc29sdXRpb24gZm9yIHRoZSBwYXRoLCBzbWFsbGVyIG51bWJlcnMgY3JlYXRlIG1vcmUgcG9pbnRzIHBlciBwYXRoL3Ntb290aGVyIHBhdGhzCm5faXRlciA8LSAyMCAjIE51bWJlciBvZiBpdGVyYXRpb25zIHBlciBwYXRoCm5fcGF0aCA8LSA1MDAwICMgTnVtYmVyIG9mIHBhdGhzCgojIFNhbXBsZSBzdGFydGluZyBwb2ludHMKc3RfcG9pbnQgPC0gZGF0YS5mcmFtZSh4ID0gcnVuaWYobl9wYXRoLCBuX21pbiwgbl9tYXgpLAogICAgICAgICAgICAgICAgICAgICAgICN4ID0gMiwKICAgICAgICAgICAgICAgICAgICAgICB5ID0gcnVuaWYobl9wYXRoLCBuX21pbiwgbl9tYXgpKQoKIyBJbml0aWFsaXplIGRhdGEgZnJhbWUgd2l0aCBwYXRocwpwYXRocyA8LSBkYXRhLmZyYW1lKHggPSBudW1lcmljKCksIHkgPSBudW1lcmljKCksIHN0ZXAgPSBudW1lcmljKCksIHBhdGggPSBudW1lcmljKCkpCgpmb3IocCBpbiAxOm5fcGF0aCl7CiAgcGF0aHMgPC0gcmJpbmQocGF0aHMsCiAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZShzdF9wb2ludFtwLF0sIHN0ZXAgPSAxLCBwYXRoID0gcCkpCiAgbmV4dF94IDwtIHN0X3BvaW50W3AsICJ4Il0KICBuZXh0X3kgPC0gc3RfcG9pbnRbcCwgInkiXQogIGZvcihpIGluIDE6bl9pdGVyKXsKICAgICMgV2l0aCB0aGUgbmV4dCBwb2ludCwgcmV0cmlldmUgdGhlIGFuZ2xlIG9mIHRoZSBjbG9zZXN0IHBvaW50IGluIHRoZSBkaXJlY3Rpb24gb2YgdGhlIGZsb3cKICAgIGFuZ2xlIDwtIGRmICU+JSAKICAgICAgbXV0YXRlKGQgPSBzcXJ0KCh4IC0gbmV4dF94KV4yICsgKHkgLSBuZXh0X3kpXjIpKSAlPiUKICAgICAgZmlsdGVyKGQgPT0gbWluKGQpKSAlPiUKICAgICAgcHVsbChhbmdsZSkKICAgICMgQ2FsY3VsYXRlIHRoZSBjb29yZGluYXRlcyBvZiB0aGUgbmV3IG5leHQgcG9pbnQgYW5kIGJpbmQgdG8gcGF0aHMKICAgIHBhdGhzIDwtIHJiaW5kKHBhdGhzLAogICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZSh4ID0gbmV4dF94ICsgc2lnbihuZXh0X3gpICogcmVzICogY29zKGFuZ2xlKSwgIyBJbXBvcnRhbnQgdG8gbXVsdGlwbHkgYnkgdGhlIHNpZ24gb2YgdGhlIG5leHQgcG9pbnQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IG5leHRfeSArIHNpZ24obmV4dF95KSAqIHJlcyAqIHNpbihhbmdsZSksICMgSW1wb3J0YW50IHRvIG11bHRpcGx5IGJ5IHRoZSBzaWduIG9mIHRoZSBuZXh0IHBvaW50CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXAgPSBpICsgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0aCA9IHApKQogICAgIyBGaW5kIHRoZSBuZXh0IHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgc2VhcmNoCiAgICBuZXh0X3ggPC0gc2xpY2VfdGFpbChwYXRocykgJT4lCiAgICAgIHB1bGwoeCkKICAgIG5leHRfeSA8LSBzbGljZV90YWlsKHBhdGhzKSAlPiUKICAgICAgcHVsbCh5KQogICAgCiAgICAjIENoZWNrIHRoZSBib3VuZGFyeSBjb25kaXRpb24KICAgIGJvdW5kYXJ5X2NvbmRpdGlvbiA8LSBuZXh0X3ggPCBuX21pbiB8IG5leHRfeCA+PSBuX21heCB8CiAgICAgIG5leHRfeSA8IG5fbWluIHwgbmV4dF95ID49IG5fbWF4CiAgICAjIElmIGF0IHRoZSBib3VuZGFyeSBzZXQgaSB0byBuX2l0ZXIgdG8gY29tcGxldGUgdGhlIGxvb3AKICAgIGlmIChib3VuZGFyeV9jb25kaXRpb24pIGJyZWFrCiAgfQp9CgojIFJlbmFtZSBwYXRocwoKcGF0aHNfZXg0IDwtIHBhdGhzCmBgYAoKUGxvdCBwYXRoczoKYGBge3IgcGF0aHMtZXg0fQojIEFkZCBsZW5ndGggb2YgcGF0aCB0byB0YWJsZToKcGF0aHNfZXg0IDwtIHBhdGhzX2V4NCAlPiUgCiAgbGVmdF9qb2luKHBhdGhzX2V4NCAlPiUKICAgICAgICAgICAgICBncm91cF9ieShwYXRoKSAlPiUKICAgICAgICAgICAgICBzdW1tYXJpemUobF9wYXRoID0gbigpLCAjIGxlbmd0aCBvZiBwYXRoCiAgICAgICAgICAgICAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpLAogICAgICAgICAgICBieSA9ICJwYXRoIikgCgpnZ3Bsb3QoKSArIAogIGdlb21fbGluZShkYXRhID0gcGF0aHNfZXg0LAogICAgICAgICAgICBhZXMoeCA9IHgsCiAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgIGdyb3VwID0gcGF0aCwKICAgICAgICAgICAgICAgIGNvbG9yID0gc3RlcCAtIGxfcGF0aC8yKSwKICAgICAgICAgICAgc2l6ZSA9IDAuMSkgKwogIGdlb21fc2VnbWVudChkYXRhID0gZGYsCiAgICAgICAgICAgICAgIGFlcyh4ID0geCwKICAgICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgICAgeGVuZCA9IHhlbmQsCiAgICAgICAgICAgICAgICAgICB5ZW5kID0geWVuZCkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkZiwKICAgICAgICAgICAgIGFlcyh4ID0geGVuZCwgCiAgICAgICAgICAgICAgICAgeSA9eWVuZCkpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKYGBge3J9CmNvbF9wYWxldHRlIDwtIG1leC5icmV3ZXIoIlJldm9sdWNpb24iKQoKcGF0aHNfZXg0IDwtIHBhdGhzX2V4NCAlPiUKICBtdXRhdGUocF9ncmlkID0gKHggKiB5KV4yLAogICAgICAgICBzaXplID0gMSAvIChhYnMoc3RlcCAtIGxfcGF0aC8yKSArIDEpKQoKZ2dwbG90KCkgKyAKICBnZW9tX3JlY3QoYWVzKHhtaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB4bWF4ID0gbl9tYXgsIAogICAgICAgICAgICAgICAgeW1pbiA9IG5fbWluLCAKICAgICAgICAgICAgICAgIHltYXggPSBuX21heCksCiAgICAgICAgICAgIGZpbGwgPSAiYmxhY2siKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBwYXRoc19leDQgJT4lCiAgICAgICAgICAgICAgZmlsdGVyKHggPiBuX21pbiAmIHggPCBuX21heCwgCiAgICAgICAgICAgICAgICAgICAgIHkgPiBuX21pbiAmIHkgPCBuX21heCksCiAgICAgICAgICAgICAgYWVzKHggPSB4LAogICAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBwYXRoLAogICAgICAgICAgICAgICAgICBjb2xvciA9IHBfZ3JpZF4oMS8zKSkpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSBuX21pbiwgeGVuZCA9IG5fbWF4LCB5ID0gMCwgeWVuZCA9IDApLAogICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVsxMF0sCiAgICAgICAgICAgICBzaXplID0gMykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAwLCB5ID0gbl9taW4sIHllbmQgPSBuX21heCksCiAgICAgICAgICAgICBjb2xvciA9IGNvbF9wYWxldHRlWzEwXSwKICAgICAgICAgICAgIHNpemUgPSAzKSArCiAgZ2VvbV9jaXJjbGUoYWVzKHgwID0gMCwKICAgICAgICAgICAgICAgICAgeTAgPSAwLAogICAgICAgICAgICAgICAgICByID0gNCksCiAgICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVsxMF0sCiAgICAgICAgICAgICAgZmlsbCA9ICJibGFjayIsCiAgICAgICAgICAgICAgc2l6ZSA9IDMpICsKICBnZW9tX3JlY3QoYWVzKHhtaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB4bWF4ID0gbl9tYXgsIAogICAgICAgICAgICAgICAgeW1pbiA9IG5fbWluLCAKICAgICAgICAgICAgICAgIHltYXggPSBuX21heCksCiAgICAgICAgICAgIGZpbGwgPSBOQSwKICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVsxMF0sIAogICAgICAgICAgICBzaXplID0gMykgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBjb2xfcGFsZXR0ZSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lX3ZvaWQoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoKCiMjIEV4cGVyaW1lbnRzIHdpdGggYW5nbGUgYW5kIHJlc29sdXRpb25zCgoKYGBge3J9CiMgU2l6ZSBvZiBncmlkCm5fbWluIDwtIC0xNyAKbl9tYXggPC0gMTgKIyBMZW5ndGggb2Ygc2VnbWVudApsIDwtIHNxcnQoMikgKyAwLjEKCmRmIDwtIGRhdGEuZnJhbWUoZXhwYW5kLmdyaWQoeCA9IHNlcShuX21pbiwgbl9tYXgsIDEpLCB5ID0gc2VxKG5fbWluLCBuX21heCwgMSkpKQpgYGAKCgpDcmVhdGUgbmV3IGZsb3cgZmllbGQ6CmBgYHtyfQojIE9mZnNldHMKeF9vIDwtIC0xNQp5X28gPC0gNQppbml0X3IgPC0gMApkX2FuZ2xlIDwtIDIKCiMgcmVtb3ZlIHRoZSB2YWxlbmNlCmRmIDwtIGRmICU+JQogIG11dGF0ZShhbmdsZSA9ICgoKHggLSB4X28pICogKHkgLSB5X28pKSAvKG5fbWF4IC0gbl9taW4pXjIgKiBkX2FuZ2xlICogcGkgKyBpbml0X3IgKiBwaSksIyAqIHNpZ24oeCAqIHkpLCAjIGFkZCB0aGUgc2lnbnMKICAgICAgICAgeGVuZCA9ICh4ICsgbCAqIGNvcyhhbmdsZSkpLAogICAgICAgICB5ZW5kID0gKHkgKyBsICogc2luKGFuZ2xlKSkpCgpkZiAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwKICAgICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgICAgeGVuZCA9IHhlbmQsCiAgICAgICAgICAgICAgICAgICB5ZW5kID0geWVuZCkpICsKICBnZW9tX3BvaW50KGFlcyh4ID0geGVuZCwgCiAgICAgICAgICAgICAgICAgeSA9eWVuZCkpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkKYGBgCgpBZGQgb2JzdGFjbGVzOgpgYGB7cn0KZGYgPC0gZGYgJT4lCiAgbXV0YXRlKHIxID0gc3FydCgoeCAtIDgpXjIgKyAoeSAtIDgpXjIpLAogICAgICAgICByMiA9IHNxcnQoKHggKyA4KV4yICsgKHkgKyA4KV4yKSkgJT4lCiAgZmlsdGVyKHIxID4gNCwKICAgICAgICAgcjIgPiA0KQoKZGYgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHgsCiAgICAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgICAgIHhlbmQgPSB4ZW5kLAogICAgICAgICAgICAgICAgICAgeWVuZCA9IHllbmQpKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHhlbmQsIAogICAgICAgICAgICAgICAgIHkgPXllbmQpKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfdm9pZCgpCgpgYGAKCkNoYW5nZSB0aGUgcmVzb2x1dGlvbjoKYGBge3J9CiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQojc2V0LnNlZWQoMzQ2MzYzKQoKIyBQYXJhbWV0ZXJzIGZvciBjcmVhdGluZyB0aGUgcGF0aHMKcmVzIDwtIDMgIyBSZXNvbHV0aW9uIGZvciB0aGUgcGF0aCwgc21hbGxlciBudW1iZXJzIGNyZWF0ZSBtb3JlIHBvaW50cyBwZXIgcGF0aC9zbW9vdGhlciBwYXRocwpuX2l0ZXIgPC0gNTAgIyBOdW1iZXIgb2YgaXRlcmF0aW9ucyBwZXIgcGF0aApuX3BhdGggPC0gNTAwMCAjIE51bWJlciBvZiBwYXRocwoKIyBTYW1wbGUgc3RhcnRpbmcgcG9pbnRzCnN0X3BvaW50IDwtIGRhdGEuZnJhbWUoeCA9IHJ1bmlmKG5fcGF0aCwgbl9taW4sIG5fbWF4KSwKICAgICAgICAgICAgICAgICAgICAgICAjeCA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgeSA9IHJ1bmlmKG5fcGF0aCwgbl9taW4sIG5fbWF4KSkgJT4lCiAgbXV0YXRlKHIxID0gc3FydCgoeCAtIDgpXjIgKyAoeSAtIDgpXjIpLAogICAgICAgICByMiA9IHNxcnQoKHggKyA4KV4yICsgKHkgKyA4KV4yKSkgJT4lCiAgZmlsdGVyKHIxID4gNCwKICAgICAgICAgcjIgPiA0KSAlPiUKICBzZWxlY3QoLXN0YXJ0c193aXRoKCJyIikpCgojIFVwZGF0ZSBudW1iZXIgb2YgcGF0aHMgdG8gYWNjb3VudCBmb3IgdGhlIHN0YXJ0aW5nIHBvaW5zIGVsaW1pbmF0ZWQgZHVlIHRvIG9ic3RhY2xlcwpuX3BhdGggPC0gbnJvdyhzdF9wb2ludCkKCiMgSW5pdGlhbGl6ZSBkYXRhIGZyYW1lIHdpdGggcGF0aHMKcGF0aHMgPC0gZGF0YS5mcmFtZSh4ID0gbnVtZXJpYygpLCB5ID0gbnVtZXJpYygpLCBzdGVwID0gbnVtZXJpYygpLCBwYXRoID0gbnVtZXJpYygpKQoKZm9yKHAgaW4gMTpuX3BhdGgpewogIHBhdGhzIDwtIHJiaW5kKHBhdGhzLAogICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoc3RfcG9pbnRbcCxdLCBzdGVwID0gMSwgcGF0aCA9IHApKQogIG5leHRfeCA8LSBzdF9wb2ludFtwLCAieCJdCiAgbmV4dF95IDwtIHN0X3BvaW50W3AsICJ5Il0KICBmb3IoaSBpbiAxOm5faXRlcil7CiAgICAjIFdpdGggdGhlIG5leHQgcG9pbnQsIHJldHJpZXZlIHRoZSBhbmdsZSBvZiB0aGUgY2xvc2VzdCBwb2ludCBpbiB0aGUgZGlyZWN0aW9uIG9mIHRoZSBmbG93CiAgICBhbmdsZSA8LSBkZiAlPiUgCiAgICAgIG11dGF0ZShkID0gc3FydCgoeCAtIG5leHRfeCleMiArICh5IC0gbmV4dF95KV4yKSkgJT4lCiAgICAgIGZpbHRlcihkID09IG1pbihkKSkKICAgIG1pbl9kIDwtIGFuZ2xlICU+JSBwdWxsKGQpCiAgICBhbmdsZSA8LSBhbmdsZSAlPiUgcHVsbChhbmdsZSkKICAgICMgQ2FsY3VsYXRlIHRoZSBjb29yZGluYXRlcyBvZiB0aGUgbmV3IG5leHQgcG9pbnQgYW5kIGJpbmQgdG8gcGF0aHMKICAgIHBhdGhzIDwtIHJiaW5kKHBhdGhzLAogICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZSh4ID0gbmV4dF94ICsgcmVzICogY29zKGFuZ2xlKSwgIyBSZW1vdmVkIHRoZSB2YWxlbmNlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBuZXh0X3kgKyByZXMgKiBzaW4oYW5nbGUpLCAjIFJlbW92ZWQgdGhlIHZhbGVuY2UKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcCA9IGkgKyAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXRoID0gcCkpCiAgICAjIEZpbmQgdGhlIG5leHQgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBzZWFyY2gKICAgIG5leHRfeCA8LSBzbGljZV90YWlsKHBhdGhzKSAlPiUKICAgICAgcHVsbCh4KQogICAgbmV4dF95IDwtIHNsaWNlX3RhaWwocGF0aHMpICU+JQogICAgICBwdWxsKHkpCiAgICAKICAgICMgQ2hlY2sgdGhlIGJvdW5kYXJ5IGNvbmRpdGlvbgogICAgYm91bmRhcnlfY29uZGl0aW9uIDwtIG5leHRfeCA8IG5fbWluIHwgbmV4dF94ID49IG5fbWF4IHwKICAgICAgbmV4dF95IDwgbl9taW4gfCBuZXh0X3kgPj0gbl9tYXggfCBtaW5fZCA+IHNxcnQoMikgKyAwLjEKICAgICMgSWYgYXQgdGhlIGJvdW5kYXJ5IHNldCBpIHRvIG5faXRlciB0byBjb21wbGV0ZSB0aGUgbG9vcAogICAgaWYgKGJvdW5kYXJ5X2NvbmRpdGlvbikgYnJlYWsKICB9Cn0KCiMgUmVuYW1lIHBhdGhzCgpwYXRoc19leDUgPC0gcGF0aHMKYGBgCgpQbG90IHBhdGhzOgpgYGB7ciBwYXRocy1leDV9CiMgQWRkIGxlbmd0aCBvZiBwYXRoIHRvIHRhYmxlOgpwYXRoc19leDUgPC0gcGF0aHNfZXg1ICU+JSAKICBsZWZ0X2pvaW4ocGF0aHNfZXg1ICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KHBhdGgpICU+JQogICAgICAgICAgICAgIHN1bW1hcml6ZShsX3BhdGggPSBuKCksICMgbGVuZ3RoIG9mIHBhdGgKICAgICAgICAgICAgICAgICAgICAgICAgLmdyb3VwcyA9ICJkcm9wIiksCiAgICAgICAgICAgIGJ5ID0gInBhdGgiKSAKCmdncGxvdCgpICsgCiAgZ2VvbV9wYXRoKGRhdGEgPSBwYXRoc19leDUsCiAgICAgICAgICAgIGFlcyh4ID0geCwKICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgZ3JvdXAgPSBwYXRoKSwKICAgICAgICAgICAgY29sb3IgPSAiYmx1ZSIsCiAgICAgICAgICAgIHNpemUgPSAxKSArCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBkZiwKICAgICAgICAgICAgICAgYWVzKHggPSB4LAogICAgICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICAgICB4ZW5kID0geGVuZCwKICAgICAgICAgICAgICAgICAgIHllbmQgPSB5ZW5kKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRmLAogICAgICAgICAgICAgYWVzKHggPSB4ZW5kLCAKICAgICAgICAgICAgICAgICB5ID15ZW5kKSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lX3ZvaWQoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpPYnN0YWNsZSBhdm9pZGFuY2Ugbm90IHdvcmtpbmcgZ3JlYXQsIHdvbmRlciB3aHkuCgpQcmV0dGlmeToKYGBge3J9CmNvbF9wYWxldHRlIDwtIG1leC5icmV3ZXIoIlJldm9sdWNpb24iKQoKcGF0aHNfZXg1IDwtIHBhdGhzX2V4NSAlPiUKICBtdXRhdGUocF9ncmlkID0gKCh4IC0gbl9taW4pICogKHkgLSBuX21pbiApKV4yLAogICAgICAgICBzaXplID0gMSAvIChhYnMoc3RlcCAtIGxfcGF0aC8yKSArIDEpKQoKcGxvdDEgPC0gZ2dwbG90KCkgKyAKICBnZW9tX3JlY3QoYWVzKHhtaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB4bWF4ID0gbl9tYXgsIAogICAgICAgICAgICAgICAgeW1pbiA9IG5fbWluLCAKICAgICAgICAgICAgICAgIHltYXggPSBuX21heCksCiAgICAgICAgICAgIGZpbGwgPSAiYmxhY2siKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBwYXRoc19leDUgJT4lCiAgICAgICAgICAgICAgZmlsdGVyKHggPiBuX21pbiAmIHggPCBuX21heCwKICAgICAgICAgICAgICAgICAgICAgeSA+IG5fbWluICYgeSA8IG5fbWF4KSwKICAgICAgICAgICAgICBhZXMoeCA9IHgsCiAgICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgICBncm91cCA9IHBhdGgsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gcF9ncmlkXigxLzMpKSkgKwogIGdlb21fY2lyY2xlKGFlcyh4MCA9IC04LAogICAgICAgICAgICAgICAgICB5MCA9IC04LAogICAgICAgICAgICAgICAgICByID0gNCksCiAgICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVs0XSwKICAgICAgICAgICAgICBmaWxsID0gY29sX3BhbGV0dGVbOF0sCiAgICAgICAgICAgICAgc2l6ZSA9IDMpICsKICAgZ2VvbV9jaXJjbGUoYWVzKHgwID0gOCwKICAgICAgICAgICAgICAgICAgIHkwID0gOCwKICAgICAgICAgICAgICAgICAgIHIgPSA0KSwKICAgICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVs0XSwKICAgICAgICAgICAgICAgZmlsbCA9IGNvbF9wYWxldHRlWzFdLAogICAgICAgICAgICAgICBzaXplID0gMykgKwogIGdlb21fcmVjdChhZXMoeG1pbiA9IG5fbWluLCAKICAgICAgICAgICAgICAgIHhtYXggPSBuX21heCwgCiAgICAgICAgICAgICAgICB5bWluID0gbl9taW4sIAogICAgICAgICAgICAgICAgeW1heCA9IG5fbWF4KSwKICAgICAgICAgICAgZmlsbCA9IE5BLAogICAgICAgICAgICBjb2xvciA9IGNvbF9wYWxldHRlWzhdLCAKICAgICAgICAgICAgc2l6ZSA9IDMpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gY29sX3BhbGV0dGUpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwbG90MQpgYGAKCmBgYHtyfQpjb2xfcGFsZXR0ZSA8LSBtZXguYnJld2VyKCJBdGVudGFkbyIpCgpwYXRoc19leDUgPC0gcGF0aHNfZXg1ICU+JQogIG11dGF0ZShwX2dyaWQgPSAoKHggLSBuX21pbikgKiAoeSAtIG5fbWluICkpXjIsCiAgICAgICAgIHNpemUgPSAxIC8gKGFicyhzdGVwIC0gbF9wYXRoLzIpICsgMSkpCgpwbG90MiA8LSBnZ3Bsb3QoKSArIAogIGdlb21fcmVjdChhZXMoeG1pbiA9IC1uX21pbiwgCiAgICAgICAgICAgICAgICB4bWF4ID0gLW5fbWF4LCAKICAgICAgICAgICAgICAgIHltaW4gPSBuX21pbiwgCiAgICAgICAgICAgICAgICB5bWF4ID0gbl9tYXgpLAogICAgICAgICAgICBmaWxsID0gImJsYWNrIikgKwogIGdlb21fcGF0aChkYXRhID0gcGF0aHNfZXg1ICU+JQogICAgICAgICAgICAgIGZpbHRlcih4ID4gbl9taW4gJiB4IDwgbl9tYXgsCiAgICAgICAgICAgICAgICAgICAgIHkgPiBuX21pbiAmIHkgPCBuX21heCksCiAgICAgICAgICAgICAgYWVzKHggPSAteCwKICAgICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICAgIGdyb3VwID0gcGF0aCwKICAgICAgICAgICAgICAgICAgY29sb3IgPSBwX2dyaWReKDEvMykpKSArCiAgZ2VvbV9jaXJjbGUoYWVzKHgwID0gOCwKICAgICAgICAgICAgICAgICAgeTAgPSAtOCwKICAgICAgICAgICAgICAgICAgciA9IDQpLAogICAgICAgICAgICAgIGNvbG9yID0gY29sX3BhbGV0dGVbNF0sCiAgICAgICAgICAgICAgZmlsbCA9IGNvbF9wYWxldHRlWzhdLAogICAgICAgICAgICAgIHNpemUgPSAzKSArCiAgIGdlb21fY2lyY2xlKGFlcyh4MCA9IC04LAogICAgICAgICAgICAgICAgICAgeTAgPSA4LAogICAgICAgICAgICAgICAgICAgciA9IDQpLAogICAgICAgICAgICAgICBjb2xvciA9IGNvbF9wYWxldHRlWzRdLAogICAgICAgICAgICAgICBmaWxsID0gY29sX3BhbGV0dGVbMV0sCiAgICAgICAgICAgICAgIHNpemUgPSAzKSArCiAgZ2VvbV9yZWN0KGFlcyh4bWluID0gLW5fbWluLCAKICAgICAgICAgICAgICAgIHhtYXggPSAtbl9tYXgsIAogICAgICAgICAgICAgICAgeW1pbiA9IG5fbWluLCAKICAgICAgICAgICAgICAgIHltYXggPSBuX21heCksCiAgICAgICAgICAgIGZpbGwgPSBOQSwKICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVs4XSwgCiAgICAgICAgICAgIHNpemUgPSAzKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbF9wYWxldHRlKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfdm9pZCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcGxvdDIKYGBgCgpgYGB7cn0KY29sX3BhbGV0dGUgPC0gbWV4LmJyZXdlcigiQWxhY2VuYSIpCgpwYXRoc19leDUgPC0gcGF0aHNfZXg1ICU+JQogIG11dGF0ZShwX2dyaWQgPSAoKHggLSBuX21pbikgKiAoeSAtIG5fbWluICkpXjIsCiAgICAgICAgIHNpemUgPSAxIC8gKGFicyhzdGVwIC0gbF9wYXRoLzIpICsgMSkpCgpwbG90MyA8LSBnZ3Bsb3QoKSArIAogIGdlb21fcmVjdChhZXMoeG1pbiA9IG5fbWluLCAKICAgICAgICAgICAgICAgIHhtYXggPSBuX21heCwgCiAgICAgICAgICAgICAgICB5bWluID0gLW5fbWluLCAKICAgICAgICAgICAgICAgIHltYXggPSAtbl9tYXgpLAogICAgICAgICAgICBmaWxsID0gImJsYWNrIikgKwogIGdlb21fcGF0aChkYXRhID0gcGF0aHNfZXg1ICU+JQogICAgICAgICAgICAgIGZpbHRlcih4ID4gbl9taW4gJiB4IDwgbl9tYXgsCiAgICAgICAgICAgICAgICAgICAgIHkgPiBuX21pbiAmIHkgPCBuX21heCksCiAgICAgICAgICAgICAgYWVzKHggPSB4LAogICAgICAgICAgICAgICAgICB5ID0gLXksCiAgICAgICAgICAgICAgICAgIGdyb3VwID0gcGF0aCwKICAgICAgICAgICAgICAgICAgY29sb3IgPSBwX2dyaWReKDEvMykpKSArCiAgZ2VvbV9jaXJjbGUoYWVzKHgwID0gOCwKICAgICAgICAgICAgICAgICAgeTAgPSAtOCwKICAgICAgICAgICAgICAgICAgciA9IDQpLAogICAgICAgICAgICAgIGNvbG9yID0gY29sX3BhbGV0dGVbNF0sCiAgICAgICAgICAgICAgZmlsbCA9IGNvbF9wYWxldHRlWzhdLAogICAgICAgICAgICAgIHNpemUgPSAzKSArCiAgIGdlb21fY2lyY2xlKGFlcyh4MCA9IC04LAogICAgICAgICAgICAgICAgICAgeTAgPSA4LAogICAgICAgICAgICAgICAgICAgciA9IDQpLAogICAgICAgICAgICAgICBjb2xvciA9IGNvbF9wYWxldHRlWzRdLAogICAgICAgICAgICAgICBmaWxsID0gY29sX3BhbGV0dGVbMV0sCiAgICAgICAgICAgICAgIHNpemUgPSAzKSArCiAgZ2VvbV9yZWN0KGFlcyh4bWluID0gbl9taW4sIAogICAgICAgICAgICAgICAgeG1heCA9IG5fbWF4LCAKICAgICAgICAgICAgICAgIHltaW4gPSAtbl9taW4sIAogICAgICAgICAgICAgICAgeW1heCA9IC1uX21heCksCiAgICAgICAgICAgIGZpbGwgPSBOQSwKICAgICAgICAgICAgY29sb3IgPSBjb2xfcGFsZXR0ZVs4XSwgCiAgICAgICAgICAgIHNpemUgPSAzKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbF9wYWxldHRlKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfdm9pZCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcGxvdDMKYGBgCgoKYGBge3J9CmNvbF9wYWxldHRlIDwtIG1leC5icmV3ZXIoIlJvbmRhIikKCnBhdGhzX2V4NSA8LSBwYXRoc19leDUgJT4lCiAgbXV0YXRlKHBfZ3JpZCA9ICgoeCAtIG5fbWluKSAqICh5IC0gbl9taW4gKSleMiwKICAgICAgICAgc2l6ZSA9IDEgLyAoYWJzKHN0ZXAgLSBsX3BhdGgvMikgKyAxKSkKCnBsb3Q0IDwtIGdncGxvdCgpICsgCiAgZ2VvbV9yZWN0KGFlcyh4bWluID0gLW5fbWluLCAKICAgICAgICAgICAgICAgIHhtYXggPSAtbl9tYXgsIAogICAgICAgICAgICAgICAgeW1pbiA9IC1uX21pbiwgCiAgICAgICAgICAgICAgICB5bWF4ID0gLW5fbWF4KSwKICAgICAgICAgICAgZmlsbCA9ICJibGFjayIpICsKICBnZW9tX3BhdGgoZGF0YSA9IHBhdGhzX2V4NSAlPiUKICAgICAgICAgICAgICBmaWx0ZXIoeCA+IG5fbWluICYgeCA8IG5fbWF4LAogICAgICAgICAgICAgICAgICAgICB5ID4gbl9taW4gJiB5IDwgbl9tYXgpLAogICAgICAgICAgICAgIGFlcyh4ID0gLXgsCiAgICAgICAgICAgICAgICAgIHkgPSAteSwKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBwYXRoLAogICAgICAgICAgICAgICAgICBjb2xvciA9IHBfZ3JpZF4oMS8zKSkpICsKICBnZW9tX2NpcmNsZShhZXMoeDAgPSAtOCwKICAgICAgICAgICAgICAgICAgeTAgPSAtOCwKICAgICAgICAgICAgICAgICAgciA9IDQpLAogICAgICAgICAgICAgIGNvbG9yID0gY29sX3BhbGV0dGVbNF0sCiAgICAgICAgICAgICAgZmlsbCA9IGNvbF9wYWxldHRlWzhdLAogICAgICAgICAgICAgIHNpemUgPSAzKSArCiAgIGdlb21fY2lyY2xlKGFlcyh4MCA9IDgsCiAgICAgICAgICAgICAgICAgICB5MCA9IDgsCiAgICAgICAgICAgICAgICAgICByID0gNCksCiAgICAgICAgICAgICAgIGNvbG9yID0gY29sX3BhbGV0dGVbNF0sCiAgICAgICAgICAgICAgIGZpbGwgPSBjb2xfcGFsZXR0ZVsxXSwKICAgICAgICAgICAgICAgc2l6ZSA9IDMpICsKICBnZW9tX3JlY3QoYWVzKHhtaW4gPSAtbl9taW4sIAogICAgICAgICAgICAgICAgeG1heCA9IC1uX21heCwgCiAgICAgICAgICAgICAgICB5bWluID0gLW5fbWluLCAKICAgICAgICAgICAgICAgIHltYXggPSAtbl9tYXgpLAogICAgICAgICAgICBmaWxsID0gTkEsCiAgICAgICAgICAgIGNvbG9yID0gY29sX3BhbGV0dGVbOF0sIAogICAgICAgICAgICBzaXplID0gMykgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBjb2xfcGFsZXR0ZSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lX3ZvaWQoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnBsb3Q0CmBgYAoKYGBge3IgZmlnLmhlaWdodD0xMH0KbGlicmFyeShwYXRjaHdvcmspCgoocGxvdDEgKyBwbG90MikgLyAocGxvdDMgKyBwbG90NCkKYGBgCgo=